package com.btb.adapter.service;

import java.math.BigInteger;
import java.util.Date;
import java.util.Objects;
import java.util.Optional;
import java.util.UUID;

import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.client.RestClientException;
import org.springframework.web.client.RestTemplate;
import org.web3j.protocol.Web3j;
import org.web3j.protocol.core.methods.response.TransactionReceipt;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.TypeReference;
import com.btb.adapter.constant.Constants;
import com.btb.adapter.exception.GameException;
import com.btb.adapter.model.TaskConfig;
import com.btb.adapter.model.TransferRecord;
import com.btb.adapter.utils.ResponseText;
import com.btb.adapter.utils.Web3jUtils;
import com.btb.adapter.vo.request.PayRequest;
import com.btb.adapter.vo.response.CommonResponse;
import com.mysql.fabric.xmlrpc.base.Data;

import lombok.extern.slf4j.Slf4j;

/**
 * @author hqbzl 远程通知
 */
@Slf4j
@Service
public class NotifyService {
	@Value("${adapter.pay.url}")
	private String payUrl;
	@Value("${adapter.request.sign}")
	private String sign;
	@Autowired
	private RestTemplate restTemplate;
	@Autowired
	private TransferRecordService transferRecordService;
	@Autowired
	private WatchTransferService watchTransferService;
	@Autowired
	private Web3j web3j;

	/**
	 *
	 *  判断事件收到的区块和当前的区块的偏移量是不是比设置的大
	 *
	 *   如果区块检查有问题,以后都不会在进行推送
	 *
	 *
	 * @param transferRecord
	 * @param taskConfig
	 */
	@Transactional(rollbackFor = Exception.class)
	public void pay(final TransferRecord transferRecord, final TaskConfig taskConfig) {
		// 最新的区块高度
		final BigInteger maxBlockNumber = watchTransferService.getBlockNumber();
		if(Objects.isNull(maxBlockNumber)) {
			return;
		}
		// 设置的偏移量
		final Long offset = Optional.ofNullable(taskConfig.getOffset()).orElse(0L);
		// 收到支付事件时的区块高度
		final Long blockNumber = transferRecord.getBlockNumber();
		// 实际的差值
		long actOffset = maxBlockNumber.longValue() - blockNumber.longValue();
		if(actOffset < offset) {
			String notifyCheckResult = String.format("config offset:%s, actOffset:%s check is false", offset, actOffset);
			transferRecordService.updateConfirmPay(transferRecord.getId(), notifyCheckResult);
			return;
		}
		// 防止链上撤消, 检查失败的, 再也不会去尝试推送了
		// 此处也是有可能有异常的, 交给框架了
		if(!againCheckTransfer(transferRecord)) {
			log.error("againCheckTransfer is rollback id:{},transactionHash:{}", transferRecord.getId(),transferRecord.getTransactionHash());
			transferRecordService.updateConfirmPay(transferRecord.getId(), "rollback");
			return;
		}
		String notifyStatus = Constants.SUCCESS;  //请求的结果,是不是可以正常访问
		String response = StringUtils.EMPTY;
		String notifyResult = StringUtils.EMPTY;  //  推送的结果,包含响应的解析, 是否可以解析出成功的响应
		final PayRequest payRequest = buildPayRequest(transferRecord);
		try {
			String body = confirmPayHttpRequet(payRequest);
			response = body;
			if(checkResponse(body)) {
				notifyResult = Constants.SUCCESS;
			}
		} catch (Exception e) {
			notifyStatus = Constants.FAIL;
			response = e.getMessage();
		}
		transferRecordService.updateConfirmPay(transferRecord.getId(), payRequest,maxBlockNumber.longValue(), notifyStatus, response, notifyResult);
	}
	private boolean checkResponse(String body) {
		CommonResponse<String> parseObject = JSON.parseObject(body, new TypeReference<CommonResponse<String>>(){});
		return parseObject.isOk();
	}
	private boolean againCheckTransfer(final TransferRecord transferRecord) {
		try {
			TransactionReceipt result = Web3jUtils.query(web3j, transferRecord.getTransactionHash());
			if(Objects.isNull(result)) {
				return false;
			}
			return result.isStatusOK();
		} catch (Exception e) {
			log.error("againCheckTransfer err: {}", e.getMessage(), e);
			throw new GameException(e.getMessage());
		}
	}
	private String confirmPayHttpRequet(final PayRequest payRequest) {
		HttpHeaders headers = new HttpHeaders();
		headers.add("sign", sign);
		headers.setContentType(MediaType.APPLICATION_JSON);
		String content = JSON.toJSONString(payRequest);
		HttpEntity<String> request = new HttpEntity<>(content, headers);
		try {
			ResponseEntity<String> postForEntity = restTemplate.postForEntity(payUrl, request, String.class);
			return  postForEntity.getBody();
		} catch (Exception e) {
			log.error("confirmPayHttpRequet err: {}", e.getMessage(), e);
			throw new  GameException(e.getMessage());
		}
	}
	private PayRequest buildPayRequest(final TransferRecord transferRecord) {
		return PayRequest.builder()
				.requestId(UUID.randomUUID().toString())
				.transactionHash(transferRecord.getTransactionHash())
				.address(transferRecord.getFromAddress())
				.status(Boolean.parseBoolean(transferRecord.getStatus()))
				.orderId(transferRecord.getPayOrderId())
				.type(transferRecord.getPayType())
				.orderPrice(transferRecord.getPayOrderPrice().stripTrailingZeros().toPlainString())
				.time(Long.parseLong(transferRecord.getTimestamp()))
				.build();
	}

}
